#!/usr/bin/env python3
"""
RocketMQ 生产级配置管理系统

功能特性：
- 支持多种配置源（环境变量、配置文件、命令行参数）
- 配置验证和类型转换
- 环境感知配置
- 配置热更新
- 敏感信息保护

Author: Python RocketMQ Guide
Date: 2025-08-18
"""

import os
import yaml
import json
import logging
from typing import Dict, Any, Optional, Union, List
from dataclasses import dataclass, field
from pathlib import Path
import threading
import time

logger = logging.getLogger(__name__)


@dataclass
class RocketMQConfig:
    """RocketMQ 配置数据类"""

    # 连接配置
    name_server_address: str = "localhost:9876"
    access_key: Optional[str] = None
    secret_key: Optional[str] = None

    # 生产者配置
    producer_group: str = "DefaultProducerGroup"
    producer_max_message_size: int = 4 * 1024 * 1024  # 4MB
    producer_send_timeout: int = 3000  # 3秒
    producer_retry_times: int = 3
    producer_compress_over_size: int = 4096  # 4KB

    # 消费者配置
    consumer_group: str = "DefaultConsumerGroup"
    consumer_message_model: str = "CLUSTERING"  # CLUSTERING 或 BROADCASTING
    consumer_thread_count: int = 20
    consumer_max_reconsume_times: int = 16
    consumer_pull_timeout: int = 30000  # 30秒
    consumer_pull_batch_size: int = 32

    # 连接池配置
    connection_pool_size: int = 5
    connection_pool_timeout: int = 30
    connection_pool_max_idle_time: int = 300  # 5分钟

    # 重试配置
    retry_max_attempts: int = 3
    retry_backoff_multiplier: float = 2.0
    retry_max_backoff: int = 60  # 秒
    retry_jitter: bool = True

    # 监控配置
    enable_metrics: bool = True
    metrics_port: int = 8090
    enable_health_check: bool = True
    health_check_interval: int = 30

    # 日志配置
    log_level: str = "INFO"
    log_format: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    enable_request_logging: bool = False

    # 环境配置
    environment: str = "development"
    debug: bool = False

    def __post_init__(self):
        """配置验证和后处理"""
        self._validate_config()

    def _validate_config(self):
        """验证配置的合理性"""
        if not self.name_server_address:
            raise ValueError("name_server_address 不能为空")

        if self.producer_max_message_size <= 0:
            raise ValueError("producer_max_message_size 必须大于 0")

        if self.producer_send_timeout <= 0:
            raise ValueError("producer_send_timeout 必须大于 0")

        if self.consumer_thread_count <= 0:
            raise ValueError("consumer_thread_count 必须大于 0")

        if self.connection_pool_size <= 0:
            raise ValueError("connection_pool_size 必须大于 0")

        if self.consumer_message_model not in ["CLUSTERING", "BROADCASTING"]:
            raise ValueError("consumer_message_model 必须是 CLUSTERING 或 BROADCASTING")


class ConfigManager:
    """配置管理器 - 支持多种配置源和动态更新"""

    def __init__(self, config_file: Optional[str] = None, env_prefix: str = "ROCKETMQ"):
        """
        初始化配置管理器

        Args:
            config_file: 配置文件路径
            env_prefix: 环境变量前缀
        """
        self.config_file = config_file
        self.env_prefix = env_prefix
        self._config = None
        self._lock = threading.RLock()
        self._watchers = []
        self._last_modified = None

        # 初始化日志
        self._setup_logging()

        # 加载配置
        self.reload_config()

        # 启动配置文件监控（如果指定了配置文件）
        if config_file:
            self._start_file_watcher()

    def _setup_logging(self):
        """设置日志配置"""
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        )

    def get_config(self) -> RocketMQConfig:
        """获取当前配置"""
        with self._lock:
            return self._config

    def reload_config(self):
        """重新加载配置"""
        with self._lock:
            try:
                # 构建基础配置
                config_dict = self._build_base_config()

                # 从配置文件加载
                if self.config_file and Path(self.config_file).exists():
                    file_config = self._load_config_file()
                    config_dict.update(file_config)

                # 从环境变量加载
                env_config = self._load_env_config()
                config_dict.update(env_config)

                # 创建配置对象
                new_config = RocketMQConfig(**config_dict)

                # 更新日志级别
                logging.getLogger().setLevel(getattr(logging, new_config.log_level.upper()))

                old_config = self._config
                self._config = new_config

                # 通知配置变更
                if old_config is not None:
                    self._notify_config_change(old_config, new_config)

                logger.info("配置重新加载成功")

            except Exception as e:
                logger.error(f"配置加载失败: {e}")
                if self._config is None:
                    # 如果从未成功加载过配置，则使用默认配置
                    self._config = RocketMQConfig()
                    logger.warning("使用默认配置")

    def _build_base_config(self) -> Dict[str, Any]:
        """构建基础配置"""
        return {
            "environment": os.getenv("ENV", "development"),
            "debug": os.getenv("DEBUG", "false").lower() == "true"
        }

    def _load_config_file(self) -> Dict[str, Any]:
        """从配置文件加载配置"""
        try:
            with open(self.config_file, 'r', encoding='utf-8') as f:
                if self.config_file.endswith(('.yml', '.yaml')):
                    return yaml.safe_load(f) or {}
                elif self.config_file.endswith('.json'):
                    return json.load(f) or {}
                else:
                    logger.warning(f"不支持的配置文件格式: {self.config_file}")
                    return {}
        except Exception as e:
            logger.error(f"读取配置文件失败 {self.config_file}: {e}")
            return {}

    def _load_env_config(self) -> Dict[str, Any]:
        """从环境变量加载配置"""
        config = {}
        prefix = f"{self.env_prefix}_"

        # 定义环境变量映射
        env_mapping = {
            "NAME_SERVER_ADDRESS": "name_server_address",
            "ACCESS_KEY": "access_key",
            "SECRET_KEY": "secret_key",
            "PRODUCER_GROUP": "producer_group",
            "CONSUMER_GROUP": "consumer_group",
            "LOG_LEVEL": "log_level",
            "ENABLE_METRICS": "enable_metrics",
            "DEBUG": "debug"
        }

        for env_key, config_key in env_mapping.items():
            full_key = prefix + env_key
            if full_key in os.environ:
                value = os.environ[full_key]
                # 类型转换
                if config_key in ["enable_metrics", "debug"]:
                    value = value.lower() in ['true', '1', 'yes']
                elif config_key.endswith(('_timeout', '_size', '_count', '_times', '_port')):
                    try:
                        value = int(value)
                    except ValueError:
                        logger.warning(f"无法转换环境变量 {full_key} 为整数: {value}")
                        continue

                config[config_key] = value

        return config

    def _start_file_watcher(self):
        """启动配置文件监控"""

        def watch_config_file():
            """监控配置文件变化"""
            while True:
                try:
                    if Path(self.config_file).exists():
                        current_modified = Path(self.config_file).stat().st_mtime
                        if self._last_modified is None:
                            self._last_modified = current_modified
                        elif current_modified != self._last_modified:
                            logger.info("检测到配置文件变化，重新加载配置")
                            self.reload_config()
                            self._last_modified = current_modified

                    time.sleep(5)  # 每5秒检查一次
                except Exception as e:
                    logger.error(f"配置文件监控出错: {e}")
                    time.sleep(10)  # 出错时延长检查间隔

        watcher_thread = threading.Thread(target=watch_config_file, daemon=True)
        watcher_thread.start()
        logger.info(f"已启动配置文件监控: {self.config_file}")

    def add_config_watcher(self, callback):
        """添加配置变更监听器"""
        self._watchers.append(callback)

    def _notify_config_change(self, old_config: RocketMQConfig, new_config: RocketMQConfig):
        """通知配置变更"""
        for callback in self._watchers:
            try:
                callback(old_config, new_config)
            except Exception as e:
                logger.error(f"配置变更回调执行失败: {e}")

    def get_environment_config(self) -> Dict[str, Any]:
        """获取环境特定配置"""
        config = self.get_config()

        # 根据环境调整配置
        if config.environment == "production":
            return {
                "log_level": "WARNING",
                "debug": False,
                "enable_request_logging": True,
                "retry_max_attempts": 5,
                "connection_pool_size": 10
            }
        elif config.environment == "staging":
            return {
                "log_level": "INFO",
                "debug": False,
                "enable_request_logging": True,
                "retry_max_attempts": 3
            }
        else:  # development
            return {
                "log_level": "DEBUG",
                "debug": True,
                "enable_request_logging": False,
                "retry_max_attempts": 2
            }


# 全局配置管理器实例
_config_manager = None


def get_config_manager(config_file: Optional[str] = None) -> ConfigManager:
    """获取配置管理器实例（单例）"""
    global _config_manager
    if _config_manager is None:
        _config_manager = ConfigManager(config_file)
    return _config_manager


def get_config() -> RocketMQConfig:
    """获取当前配置的便捷函数"""
    return get_config_manager().get_config()
